dojo.provide("miaigo.model.BoardState");

dojo.require("miaigo.utilities");
dojo.require("miaigo.model.StoneBlock");
dojo.require("miaigo.model.utils");

dojo.declare(
    "miaigo.model.BoardState",
    null,
    {
	boardSize: 19,

	// Array of Block
	blocks: null,

	// temp board map to speed up querying by coordinate, created on demand.
	// this is an object with keys looks like "2,5", "3,6" for point at (x,y)
	// and the value is an object looks like {stone:"black",block:3}.
	boardMap: null,

	constructor: function(boardSize)
	{
	    if (boardSize)
	    {
		this.boardSize = boardSize;
	    }
	    this.blocks = new Array();
	},

	getBlock: function(blockID)
	{
	    if (this.blocks && blockID >= 0 && blockID < this.blocks.length)
	    {
		return this.blocks[blockID];
	    }

	    return null;
	},

	getAllBlocks: function()
	{
	    return this.blocks;
	},

	addBlock: function(block)
	{
	    this.blocks.push(block);

	    if (this.boardMap)
	    {
		this.placeBlockToBoardMap(this.blocks.length - 1);
	    }
	},

	removeBlock: function(blockID)
	{
	    var block = this.blocks[blockID];
	    this.blocks[blockID] = null;

	    // remove block from board map?

	    return block;
	},

	removeBlocks: function(blockIDs)
	{
	    var removed = new miaigo.model.PointSet();

	    for (i in blockIDs)
	    {
		var removedBlock = this.removeBlock(blockIDs[i]);
		removed.addPoints(removedBlock.getAllPoints());
	    }

	    return removed;
	},

	stoneAt: function(point)
	{
	    if (!this.boardMap)
	    {
		this.buildBoardMap();
	    }

	    var key = miaigo.utilities.pointToString(point);
	    if (this.boardMap[key])
	    {
		return this.boardMap[key].stone;
	    }
	    else
	    {
		return null;
	    }
	},

	blockAt: function(point)
	{
	    if (!this.boardMap)
	    {
		this.buildBoardMap();
	    }

	    var key = miaigo.utilities.pointToString(point);
	    if (this.boardMap[key])
	    {
		return this.boardMap[key].block;
	    }
	    else
	    {
		return -1;
	    }
	},

	placeBlockToBoardMap: function(blockID)
	{
	    var block = this.blocks[blockID];

	    if (block)
	    {
		var points = block.getAllPoints();
		if (points && points.length > 0)
		{
		    for (i in points)
		    {
			var key = miaigo.utilities.pointToString(points[i]);
			this.boardMap[key] =
			    {
				stone: block.color,
				block: blockID
			    };
		    }
		}	    
	    }
	},

	buildBoardMap: function()
	{
	    this.boardMap = {};

	    for (i in this.blocks)
	    {
		this.placeBlockToBoardMap(i);
	    }
	},

	getBlockLives: function(blockID)
	{
	    var block = this.getBlock(blockID);
	    var points = block.getAllPoints();
	    var lives = new miaigo.model.PointSet();

	    for (i in points)
	    {
		var adjacentPoints = miaigo.model.utils.adjacentPoints(points[i]);

		for (dir in adjacentPoints)
		{
		    if (adjacentPoints[dir] && null == this.stoneAt(adjacentPoints[dir]))
		    {
			lives.addPoint(adjacentPoints[dir].x, adjacentPoints[dir].y);
		    }
		}
	    }

	    return lives;
	},

	clone: function()
	{
	    var newState = new miaigo.model.BoardState(this.boardSize);

	    // copy all blocks
	    for (i in this.blocks)
	    {
		if (this.blocks[i])
		    newState.addBlock(this.blocks[i].clone());
	    }

	    return newState;
	},

	makeMove: function(moveParam)
	{
	    var newBlock = new miaigo.model.StoneBlock(moveParam.stone);
	    newBlock.addPoint(moveParam.x, moveParam.y);

	    var adjacentPoints = miaigo.model.utils.adjacentPoints
	    (
		{x: moveParam.x, y: moveParam.y},
		this.boardSize
	    );

	    var blockIDsToMerge = new Array();

	    for (dir in adjacentPoints)
	    {
		if (moveParam.stone == this.stoneAt(adjacentPoints[dir]))
		{
		    var blockID = this.blockAt(adjacentPoints[dir]);
		    var exists = false;

		    for (i in blockIDsToMerge)
		    {
			if (blockIDsToMerge[i] == blockID)
			{
			    exists = true;
			    break;
			}
		    }

		    if (!exists)
		    {
			blockIDsToMerge.push(blockID);
		    }
		}
	    }

	    for (i in blockIDsToMerge)
	    {
		var blockIDToMerge = blockIDsToMerge[i];
		newBlock.merge(this.blocks[blockIDToMerge]);
		this.blocks[blockIDToMerge] = null;
	    }

	    this.addBlock(newBlock);
	    return true;
	}

    });